﻿#region File Header
// Crc08.cs - Computes 08-bit CRC
//
// Copyright (C) Javier Valcarce. BSD License 
#endregion

#region Using Statements
using System;
using System.Diagnostics;
#endregion

namespace Arq
{
    /// <summary>
    /// Cyclic code redundancy calculator for any 8-bit generator polynomial.
    /// 
    /// Convention: Remaindet initialized with InitValue
    /// Convention: The first serial data bit is the byte's MSB
    /// </summary>
    public class Crc08
    {
        byte polinomial;
        byte crc;
        int  count;
        public const byte InitValue = 0xFF;
        static byte[] table;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="polinomial">Generator polynomial in hexadecimal notation. For example: the 
        /// polinomial g(x) = x^8 + x^6 + x^3 + x^2 + 1 has a hexadecimal representation 0x4D 
        /// (implicit x^8 term)</param>
        public Crc08(byte polinomial)
        {
            Debug.Assert((polinomial & 0x01) == 0x01, 
                "Bad polynomial, zero order coeficcient must be 1"); 

            this.polinomial = polinomial;

            // initialized with InitValue
            Reset();

            // fill crc 8-bit table
            table = new byte[256]; // 0xFF + 1

            byte t;
            byte c;
            
            for (int r = 0; r < 256; r++)            
            {
                c = (byte) r;

                for (int j = 0; j < 8; j++)
                {
                    t = (byte)(c & 0x80);
                    c = (byte)(c <<   1);

                    if (t == 0x80)
                    {
                        c = (byte)(c ^ polinomial);
                    }
                }

                table[r] = c;
            }

            //// print values
            //for (int r = 0; r < 256; r++)
            //{
            //    Console.WriteLine("{0,3} -> 0x{1:x2}", r, table[r]);
            //}
        }

        #region Public Methods
        public void Compute(byte[] buf, int offset, int count)
        {
            for (int i = 0; i < count; i++)
            {
                crc = (byte)(crc ^ buf[offset + i]);
                crc = table[crc];
            }

            this.count += count;
        }

        public void Compute(byte b)
        {
            crc = (byte)(crc ^ b);
            crc = table[crc];

            this.count++;
        }

        /// <summary>
        /// Resets current CRC value
        /// </summary>
        public void Reset()
        {
            crc   = InitValue;
            count = 0;
        }

        /// <summary>
        /// Computes a 08-bit CRC from frame[offset] to frame[offset + count - 2] and store it at 
        /// frame[offset + count - 1]
        /// </summary>
        /// <param name="frame"></param>
        /// <param name="offset"></param>
        /// <param name="count"></param>
        public void AppendCode(byte[] frame, int offset, int count)
        {
            byte localcrc = InitValue;

            for (int i = 0; i < count - 1; i++)
            {
                localcrc = (byte)(localcrc ^ frame[offset + i]);
                localcrc = table[localcrc];
            }

            frame[offset + count - 1] = localcrc;
        }

        /// <summary>
        /// Check a frame. Computes CRC from frame[offset] to frame[offset + count - 2]. It is 
        /// supposed that the redundancy code is in the last byte of the array, 
        /// frame[offset + count - 1]
        /// </summary>
        /// <param name="frame">Frame to be checked</param>
        /// <param name="offset"></param>
        /// <param name="count"></param>
        /// <returns>True if valid frame, false if not</returns>
        public bool ValidFrame(byte[] frame, int offset, int count)
        {
            byte localcrc = InitValue;

            for (int i = 0; i < count - 1; i++)
            {
                localcrc = (byte)(localcrc ^ frame[offset + i]);
                localcrc = table[localcrc];
            }

            return (localcrc == frame[offset + count - 1]);
        }
        #endregion

        #region Propeties
        /// <summary>
        /// Current CRC computed value
        /// </summary>
        public byte Code  { get { return crc;   } }

        /// <summary>
        /// Number of octets processed
        /// </summary>
        public int  Count { get { return count; } }
        #endregion


    }
}
